/**
 * 文件上传公共工具函数
 * 提供给 ImageUpload 和 FileUpload 组件使用的公共方法
 */

// ==================== 上传配置 ====================
// 文件上传配置项 - 可根据需要调整这些参数
export const UPLOAD_CONFIG = {
  // 分片上传阈值（字节）- 超过此大小自动使用分片上传
  CHUNK_THRESHOLD: 20 * 1024 * 1024, // 20MB
  
  // 分片大小（字节）- 每个分片的大小
  CHUNK_SIZE: 5 * 1024 * 1024, // 5MB
  
  // 并发上传数 - 同时上传的分片数量
  CONCURRENCY: 3,
  
  // 重试次数 - 分片上传失败时的重试次数
  MAX_RETRIES: 3,
  
  // 重试延迟配置（毫秒）
  RETRY_DELAY: {
    BASE: 1000,    // 基础延迟1秒
    MAX: 5000,     // 最大延迟5秒
    MULTIPLIER: 2  // 指数退避倍数
  },
  
  // 超时配置（毫秒）
  TIMEOUT: {
    CHUNK_UPLOAD: 60000,  // 分片上传超时60秒
    NORMAL_UPLOAD: 300000 // 普通上传超时5分钟
  },
  
  // 错误率阈值 - 超过此比例停止上传
  ERROR_RATE_THRESHOLD: 0.5 // 50%
}

import {ElMessage} from 'element-plus'
import {
  abortMultipartUpload,
  completeMultipartUpload,
  getBatchPresignedUploadUrls,
  initMultipartUpload
} from '@/api/filemanager/multipart'
import {getFileConfigs} from '@/store/modules/fileConfig'
import {getFileUrl, uploadChunk} from '@/api/filemanager/upload'
import request from '@/utils/request'

// 文件ID计数器
let fileIdCounter = 0

/**
 * 生成唯一文件ID
 */
export function generateFileId(prefix = 'file') {
  return `${prefix}_${Date.now()}_${++fileIdCounter}`
}

/**
 * 格式化文件大小
 */
export function formatFileSize(bytes) {
  if (bytes === 0) return '0 B'
  const k = 1024
  const sizes = ['B', 'KB', 'MB', 'GB', 'TB']
  const i = Math.floor(Math.log(bytes) / Math.log(k))
  return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
}

/**
 * 检查文件类型
 */
export function checkFileType(file, allowedTypes) {
  const fileName = file.name.toLowerCase()
  const fileExt = fileName.substring(fileName.lastIndexOf('.') + 1)
  return allowedTypes.includes(fileExt)
}

/**
 * 检查文件大小
 */
export function checkFileSize(file, maxFileSize) {
  const maxBytes = maxFileSize * 1024 * 1024
  return file.size <= maxBytes
}

/**
 * 决定上传方式
 */
export function determineUploadMethod(file, threshold = UPLOAD_CONFIG.CHUNK_THRESHOLD) {
  return file.size > threshold ? 'chunk' : 'normal'
}

/**
 * 复制文件路径到剪贴板
 */
export function copyPath(path) {
  navigator.clipboard.writeText(path).then(() => {
    ElMessage.success('路径已复制到剪贴板')
  }).catch(() => {
    ElMessage.error('复制失败')
  })
}

/**
 * 生成图片预览URL
 */
export function generatePreviewUrl(file) {
  return new Promise((resolve) => {
    if (file.type.startsWith('image/')) {
      const reader = new FileReader()
      reader.onload = (e) => resolve(e.target.result)
      reader.onerror = () => resolve(null)
      reader.readAsDataURL(file)
    } else {
      resolve(null)
    }
  })
}

/**
 * 加载存储配置
 */
export async function loadStorageConfigs() {
  try {
    return await getFileConfigs({ enabledOnly: true })
  } catch (error) {
    console.error('加载存储配置失败:', error)
    return []
  }
}

/**
 * 获取文件访问URL
 */
export async function getImageUrl(filePath, configId) {
  try {
    // 构造正确的参数格式
    const params = {
      filePath: filePath
    }
    if (configId) {
      params.configId = configId
    }
    
    const response = await getFileUrl(params)
    // 后端现在统一返回 data 对象格式
    const data = response.data || response
    return data.url || data.uploadUrl || filePath
  } catch (error) {
    return filePath
  }
}

/**
 * 创建拖拽处理函数
 */
export function createDragHandlers(options = {}) {
  const { 
    disabled = () => false, 
    isUploading = () => false, 
    canAddMore = () => true,
    onDragOver = () => {},
    onDragLeave = () => {},
    onDrop = () => {}
  } = options

  function handleDragOver(event) {
    event.preventDefault()
    if (!disabled() && !isUploading() && canAddMore()) {
      onDragOver(true)
    }
  }

  function handleDragLeave(event) {
    event.preventDefault()
    onDragLeave(false)
  }

  function handleDrop(event) {
    event.preventDefault()
    onDragLeave(false)
    
    if (disabled() || isUploading() || !canAddMore()) return
    
    const files = Array.from(event.dataTransfer.files)
    onDrop(files)
  }

  return { handleDragOver, handleDragLeave, handleDrop }
}

/**
 * 创建文件验证函数
 */
export function createFileValidator(options = {}) {
  const { 
    allowedTypes = [], 
    maxFileSize = 10, 
    maxCount = 5,
    currentCount = () => 0,
    queueCount = () => 0
  } = options

  function validateFile(file) {
    // 检查数量限制
    const totalCount = currentCount() + queueCount()
    if (totalCount >= maxCount) {
      return { valid: false, error: `最多只能上传 ${maxCount} 个文件` }
    }

    // 检查文件类型
    if (!checkFileType(file, allowedTypes)) {
      return { valid: false, error: `文件 ${file.name} 类型不支持` }
    }

    // 检查文件大小
    if (!checkFileSize(file, maxFileSize)) {
      return { valid: false, error: `文件 ${file.name} 大小超过 ${maxFileSize}MB` }
    }

    return { valid: true }
  }

  function validateFiles(files, existingQueue = []) {
    const validFiles = []
    const errors = []

    for (const file of files) {
      const validation = validateFile(file)
      if (!validation.valid) {
        errors.push(validation.error)
        continue
      }

      // 检查重复文件
      const isDuplicate = existingQueue.some(queueFile =>
        queueFile.name === file.name && queueFile.size === file.size
      )
      if (isDuplicate) {
        errors.push(`文件 ${file.name} 已在队列中`)
        continue
      }

      validFiles.push(file)
    }

    return { validFiles, errors }
  }

  return { validateFile, validateFiles }
}

/**
 * 后端上传
 */
export async function uploadWithBackend(fileObj, configId, uploadDir, onProgress) {
  const formData = new FormData()
  formData.append('file', fileObj.file)
  formData.append('configId', configId)
  if (uploadDir) {
    formData.append('dirPath', uploadDir)
  }

  try {
    const response = await request({
      url: '/file/upload',
      method: 'post',
      data: formData,
      headers: {
        'Content-Type': 'multipart/form-data',
        'repeatSubmit': false
      },
      onUploadProgress: (progressEvent) => {
        const progress = (progressEvent.loaded / progressEvent.total) * 100
        fileObj.progress = progress
        if (onProgress) onProgress(progress)
      }
    })

    if (response.code === 200) {
      // 后端返回格式统一为 data 对象
      const data = response.data || response
      fileObj.filePath = data.filePath || data.url
      fileObj.url = data.uploadUrl || data.url // uploadUrl是可直接访问的链接
      fileObj.fileName = data.fileName || fileObj.name
      fileObj.size = data.fileSize || data.size || fileObj.size
    } else {
      throw new Error(response.msg || '上传失败')
    }
  } catch (error) {
    throw new Error('后端上传失败: ' + error.message)
  }
}

/**
 * 直传上传
 */
export async function uploadWithDirectUpload(fileObj, configId, uploadDir, onProgress) {
  try {
    // 获取上传令牌
    const tokenResponse = await request({
      url: '/file/token',
      method: 'post',
      data: {
        fileName: fileObj.name,
        configId: configId,
        dirPath: uploadDir || undefined,
        fileType: fileObj.type
      },
      headers: {
        'repeatSubmit': false
      }
    })

    if (tokenResponse.code !== 200) {
      throw new Error(tokenResponse.msg || '获取上传令牌失败')
    }

    const tokenData = tokenResponse.data

    // 使用预签名URL上传
    const xhr = new XMLHttpRequest()

    return new Promise((resolve, reject) => {
      xhr.upload.onprogress = (event) => {
        if (event.lengthComputable) {
          const progress = (event.loaded / event.total) * 100
          fileObj.progress = progress
          if (onProgress) onProgress(progress)
        }
      }

      xhr.onload = async () => {
        if (xhr.status === 200 || xhr.status === 204) {
          fileObj.filePath = tokenData.filePath
          // 直传成功后，需要获取文件的可访问URL
          if (tokenData.uploadUrl && tokenData.uploadUrl !== tokenData.filePath) {
            // 构建可访问的URL：从uploadUrl中提取endpoint和bucket信息
            const uploadUrl = new URL(tokenData.uploadUrl)
            const pathWithoutQuery = uploadUrl.pathname
            fileObj.url = `${tokenData.endpoint}${pathWithoutQuery}`
          } else {
            fileObj.url = tokenData.uploadUrl || tokenData.filePath
          }
          resolve()
        } else {
          reject(new Error(`上传失败: ${xhr.status}`))
        }
      }

      xhr.onerror = () => {
        reject(new Error('网络错误'))
      }

      xhr.open('PUT', tokenData.uploadUrl)

      if (tokenData.headers) {
        Object.entries(tokenData.headers).forEach(([key, value]) => {
          xhr.setRequestHeader(key, value)
        })
      }

      xhr.send(fileObj.file)
    })
  } catch (error) {
    throw new Error('直传上传失败: ' + error.message)
  }
}

/**
 * 分片上传
 */
export async function uploadFileWithChunks(fileObj, config, configId, uploadDir, chunkSize = UPLOAD_CONFIG.CHUNK_SIZE, onProgress) {
  const chunkSizeBytes = chunkSize
  const totalChunks = Math.ceil(fileObj.file.size / chunkSizeBytes)
  
  console.log(`开始分片上传: 文件大小=${fileObj.file.size}字节, 分片大小=${chunkSizeBytes}字节, 总分片数=${totalChunks}`)

  try {
    // 1. 初始化分片上传
    const initResponse = await initMultipartUpload({
      fileName: fileObj.name,
      fileSize: fileObj.file.size,
      configId: configId,
      dirPath: uploadDir || undefined,
      fileType: fileObj.type
    })

    if (initResponse.code !== 200) {
      throw new Error(initResponse.msg || '初始化分片上传失败')
    }

    fileObj.uploadId = initResponse.data.uploadId
    fileObj.filePath = initResponse.data.filePath

    // 2. 批量获取预签名URL（如果支持）
    let presignedUrls = new Map()
    // 只根据配置中的 enableDirectUpload 字段来判断，不再依赖存储类型
    const shouldUseDirectUpload = config.enableDirectUpload === 1 || config.enableDirectUpload === '1'
    fileObj.supportDirectUpload = shouldUseDirectUpload

    if (shouldUseDirectUpload) {
      const urlResponse = await getBatchPresignedUploadUrls({
        uploadId: fileObj.uploadId,
        totalParts: totalChunks,
        configId: configId,
        filePath: fileObj.filePath
      })

      if (urlResponse.code === 200 && urlResponse.data.urls) {
        urlResponse.data.urls.forEach(urlInfo => {
          presignedUrls.set(urlInfo.partNumber, urlInfo.uploadUrl)
        })
      }
    }

    // 3. 上传分片（带重试和错误处理）
    const etags = []
    const concurrency = UPLOAD_CONFIG.CONCURRENCY
    let hasErrors = false
    let uploadCancelled = false

    // 添加取消上传的标志
    fileObj.cancelUpload = () => {
      uploadCancelled = true
    }

    try {
      for (let i = 0; i < totalChunks; i += concurrency) {
        // 检查是否被取消
        if (uploadCancelled) {
          throw new Error('用户取消了上传')
        }

        const batch = []
        const batchPromises = []

        for (let j = 0; j < concurrency && i + j < totalChunks; j++) {
          const chunkIndex = i + j
          const start = chunkIndex * chunkSizeBytes
          const end = Math.min(start + chunkSizeBytes, fileObj.file.size)
          const chunk = fileObj.file.slice(start, end)
          const partNumber = chunkIndex + 1

          let uploadPromise
          if (shouldUseDirectUpload && presignedUrls.has(partNumber)) {
            uploadPromise = uploadChunkDirect(chunk, presignedUrls.get(partNumber), partNumber)
          } else {
            uploadPromise = uploadChunkToBackend(chunk, fileObj.uploadId, partNumber, configId, fileObj.filePath)
          }

          batchPromises.push(uploadPromise)
          batch.push({ partNumber, chunkIndex })
        }
        // 等待当前批次完成，如果有失败的分片，记录错误但继续其他分片
        const results = await Promise.allSettled(batchPromises)
        
        for (let k = 0; k < results.length; k++) {
          const result = results[k]
          const { partNumber } = batch[k]
          
          if (result.status === 'fulfilled') {
            etags.push(result.value)
          } else {
            hasErrors = true
            console.error(`分片 ${partNumber} 上传失败:`, result.reason.message)
            // 如果是关键错误（如认证失败），立即停止
            if (result.reason.message.includes('认证') || result.reason.message.includes('权限') || 
                result.reason.message.includes('401') || result.reason.message.includes('403')) {
              throw new Error(`分片上传失败: ${result.reason.message}`)
            }
          }
        }

        // 更新进度 - 基于已完成的批次
        const completedBatches = Math.floor(i / concurrency) + 1
        const totalBatches = Math.ceil(totalChunks / concurrency)
        const progress = Math.min((completedBatches / totalBatches) * 100, 99) // 不要显示100%，等完成后再显示
        fileObj.progress = progress
        if (onProgress) onProgress(progress)

        // 如果有错误且错误率过高，停止上传
        const completedChunks = i + batch.length
        const errorRate = (completedChunks - etags.length) / completedChunks
        if (hasErrors && errorRate > UPLOAD_CONFIG.ERROR_RATE_THRESHOLD) {
          throw new Error(`分片上传错误率过高 (${Math.round(errorRate * 100)}%)，已停止上传`)
        }
      }

      // 检查是否所有分片都成功上传
      if (etags.length !== totalChunks) {
        const missingParts = []
        for (let i = 1; i <= totalChunks; i++) {
          if (!etags.find(e => e.partNumber === i)) {
            missingParts.push(i)
          }
        }
        throw new Error(`有 ${totalChunks - etags.length} 个分片上传失败，缺失分片: ${missingParts.join(', ')}`)
      }

    } catch (uploadError) {
      console.error('分片上传过程中出现错误:', uploadError.message)
      throw uploadError
    }

    // 4. 完成分片上传
    try {
      const completeResponse = await completeMultipartUpload({
        filePath: fileObj.filePath,
        uploadId: fileObj.uploadId,
        parts: etags.sort((a, b) => a.partNumber - b.partNumber),
        configId: configId
      })

      if (completeResponse.code !== 200) {
        throw new Error(completeResponse.msg || '完成分片上传失败')
      }
      
      // 上传成功，设置进度为100%
      fileObj.progress = 100
      if (onProgress) onProgress(100)
      
    } catch (completeError) {
      console.error('完成分片上传失败:', completeError.message)
      throw new Error('完成分片上传失败: ' + completeError.message)
    }
    
  } catch (error) {
    // 发生错误时自动取消分片上传
    if (fileObj.uploadId && !uploadCancelled) {
      try {
        await abortMultipartUpload(fileObj.filePath, fileObj.uploadId, configId)
      } catch (abortError) {
        console.error('取消分片上传失败:', abortError.message)
      }
    }
    
    // 清理取消函数
    delete fileObj.cancelUpload
    throw new Error('分片上传失败: ' + error.message)
  } finally {
    // 清理取消函数
    delete fileObj.cancelUpload
  }
}

/**
 * 直接上传分片（带重试机制）
 */
export async function uploadChunkDirect(chunk, uploadUrl, partNumber, maxRetries = UPLOAD_CONFIG.MAX_RETRIES) {
  let lastError = null
  
  // 重试机制：失败后重试指定次数
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      return await new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest()

        // 设置超时时间
        xhr.timeout = UPLOAD_CONFIG.TIMEOUT.CHUNK_UPLOAD

        xhr.onload = () => {
          if (xhr.status === 200 || xhr.status === 204) {
            const etag = xhr.getResponseHeader('ETag') || xhr.getResponseHeader('etag')
            resolve({
              partNumber: partNumber,
              etag: etag ? etag.replace(/"/g, '') : `etag-${partNumber}`
            })
          } else {
            reject(new Error(`HTTP ${xhr.status}: ${xhr.statusText}`))
          }
        }

        xhr.onerror = () => {
          reject(new Error('网络连接错误'))
        }

        xhr.ontimeout = () => {
          reject(new Error('请求超时'))
        }

        xhr.open('PUT', uploadUrl)
        xhr.send(chunk)
      })
      
    } catch (error) {
      lastError = error
      
      // 如果不是最后一次重试，等待一段时间再重试
      if (attempt < maxRetries) {
        const delayMs = Math.min(
          UPLOAD_CONFIG.RETRY_DELAY.BASE * Math.pow(UPLOAD_CONFIG.RETRY_DELAY.MULTIPLIER, attempt - 1), 
          UPLOAD_CONFIG.RETRY_DELAY.MAX
        )
        await new Promise(resolve => setTimeout(resolve, delayMs))
      } else {
        // 最后一次重试失败，记录错误信息
        console.error(`分片 ${partNumber} 直传失败:`, error.message)
      }
    }
  }
  
  // 所有重试都失败了
  throw new Error(`分片 ${partNumber} 直传失败（已重试 ${maxRetries} 次）: ${lastError?.message || '未知错误'}`)
}

/**
 * 上传分片到后端（带重试机制）
 */
export async function uploadChunkToBackend(chunk, uploadId, partNumber, configId, filePath, maxRetries = UPLOAD_CONFIG.MAX_RETRIES) {
  const formData = new FormData()
  formData.append('file', chunk)
  formData.append('uploadId', uploadId)
  formData.append('partNumber', partNumber)
  
  // 添加configId和filePath参数以支持S3存储的真实ETag获取
  if (configId) {
    formData.append('configId', configId)
  }
  if (filePath) {
    formData.append('filePath', filePath)
  }

  let lastError = null
  
  // 重试机制：失败后重试3次
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      // 使用专门的分片上传API，跳过错误提示
      const response = await uploadChunk(formData, { 
        skipErrorHandler: true // 跳过全局错误处理，避免频繁弹出错误提示
      })

      if (response.code === 200) {
        return {
          partNumber: partNumber,
          etag: response.data?.etag || response.etag || `etag-${partNumber}`
        }
      } else {
        throw new Error(response.msg || '上传分片失败')
      }
    } catch (error) {
      lastError = error
      
      // 如果不是最后一次重试，等待一段时间再重试
      if (attempt < maxRetries) {
        const delayMs = Math.min(
          UPLOAD_CONFIG.RETRY_DELAY.BASE * Math.pow(UPLOAD_CONFIG.RETRY_DELAY.MULTIPLIER, attempt - 1), 
          UPLOAD_CONFIG.RETRY_DELAY.MAX
        )
        await new Promise(resolve => setTimeout(resolve, delayMs))
      } else {
        // 最后一次重试失败，记录错误信息
        console.error(`分片 ${partNumber} 上传失败:`, error.message)
      }
    }
  }
  
  // 所有重试都失败了
  throw new Error(`分片 ${partNumber} 上传失败（已重试 ${maxRetries} 次）: ${lastError?.message || '未知错误'}`)
}

/**
 * 普通上传（自动选择直传或后端）
 */
export async function uploadFileNormally(fileObj, config, configId, uploadDir, onProgress) {
  // 只根据配置中的 enableDirectUpload 字段来判断，不再依赖存储类型
  const shouldUseDirectUpload = config.enableDirectUpload === 1 || config.enableDirectUpload === '1'
  fileObj.supportDirectUpload = shouldUseDirectUpload

  if (shouldUseDirectUpload) {
    await uploadWithDirectUpload(fileObj, configId, uploadDir, onProgress)
  } else {
    await uploadWithBackend(fileObj, configId, uploadDir, onProgress)
  }
}

/**
 * 创建上传管理器
 */
export function createUploadManager(options = {}) {
  const {
    allowedTypes = [],
    maxFileSize = 1024, // 默认1GB
    maxCount = 5,
    chunkSize = UPLOAD_CONFIG.CHUNK_SIZE, // 保持字节单位
    autoUpload = true,
    maxRetries = UPLOAD_CONFIG.MAX_RETRIES,
    concurrency = UPLOAD_CONFIG.CONCURRENCY
  } = options

  // 创建文件验证器
  const validator = createFileValidator({
    allowedTypes,
    maxFileSize,
    maxCount,
    currentCount: () => options.currentCount?.() || 0,
    queueCount: () => options.queueCount?.() || 0
  })

  // 创建拖拽处理器
  const dragHandlers = createDragHandlers({
    disabled: () => options.disabled?.() || false,
    isUploading: () => options.isUploading?.() || false,
    canAddMore: () => options.canAddMore?.() || true,
    onDragOver: (isDragOver) => options.onDragOver?.(isDragOver),
    onDragLeave: (isDragOver) => options.onDragLeave?.(isDragOver),
    onDrop: (files) => options.onDrop?.(files)
  })

  // 创建带重试的上传函数
  const uploadWithRetry = {
    uploadChunkDirect: (chunk, uploadUrl, partNumber) => 
      uploadChunkDirect(chunk, uploadUrl, partNumber, maxRetries),
    uploadChunkToBackend: (chunk, uploadId, partNumber, configId, filePath) => 
      uploadChunkToBackend(chunk, uploadId, partNumber, configId, filePath, maxRetries)
  }

  return {
    validator,
    dragHandlers,
    uploadWithRetry,
    // 配置参数
    config: {
      maxRetries,
      concurrency,
      chunkSize,
      autoUpload
    },
    formatFileSize,
    determineUploadMethod,
    copyPath,
    loadStorageConfigs,
    getImageUrl,
    uploadFileWithChunks,
    uploadChunkDirect,
    uploadChunkToBackend,
    uploadFileNormally
  }
}
